package com.donger.common.idempotent;

import com.donger.common.core.utils.Res;
import com.donger.common.core.utils.Result;
import com.donger.common.idempotent.annotation.Idempotent;
import com.donger.common.idempotent.service.IdempotentService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author xyx
 * @date 2020-05-24 18:34
 */
@Aspect
@Order(600)
@Configuration
@EnableConfigurationProperties(IdempotentProperties.class)
@ConditionalOnProperty(prefix = "boot.idempotent", name = "enable", havingValue = "true", matchIfMissing = true)
@RestController
public class IdempotentAutoConfiguration {

    @Autowired
    private IdempotentProperties idempotentProperties;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IdempotentService idempotentService;

    @Bean
    public IdempotentService idempotentService() {
        return new IdempotentService(idempotentProperties, redisTemplate);
    }

    @Pointcut("@annotation(com.donger.common.idempotent.annotation.Idempotent)")
    private void idempotentPointCut() {

    }

    @Around("idempotentPointCut()")
    public Object submitCheck(ProceedingJoinPoint pjp) throws Throwable {
        //获取幂等性注解对象
        Idempotent idempotent = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(Idempotent.class);
        //幂等性未启用
        if (!idempotent.enable()) {
            return pjp.proceed();
        }
        ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        // 非Web环境
        if (Objects.isNull(attributes)) {
            return pjp.proceed();
        }
        HttpServletRequest request = attributes.getRequest();
        idempotentService.checkToken(request);
        return pjp.proceed();
    }


    /**
     * 自动生成token令牌，并将令牌存入缓存，过期时间是30s
     *
     * @return token
     */
    @GetMapping("api/idempotent/token")
    public Result<Object> generationToken() {
        return Res.ok().data(idempotentService.createToken());
    }
}
